/*
 * @Author: 王冉欣 3535957686@qq.com
 * @Date: 2025-06-27 10:17:30
 * @LastEditors: 王冉欣 3535957686@qq.com
 * @LastEditTime: 2025-06-29 09:48:42
 * @FilePath: \测试3\Calculator.java
 * @Description: 带按键音效的计算器
 */
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.JTextField;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.swing.JButton;
import javax.swing.SwingUtilities;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Font;
import java.awt.Color;
import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;

public class Calculator extends JFrame implements ActionListener{
    // 界面组件
    private JPanel north,center;
    private JTextField field;
    private JButton[] btns;
    
    // 音乐播放相关变量
    private Clip clip;// 用于控制音乐播放的Clip对象
    private String musicFilePath ="C:\\Users\\35359\\Desktop\\音乐\\20250628-43453.wav";
    
    // 按键音效相关变量
    private String keySoundPath = "C:\\Users\\35359\\Desktop\\音乐\\anjian.wav"; // 按键音效文件路径
    
    public Calculator(){
        setTitle("计算器");
        setSize(700,700);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        getContentPane().setBackground(new Color(240, 240, 240));
        initComponent();
        setVisible(true);
    }

   private void initComponent(){
    north=new JPanel();
    center=new JPanel();
    field=new  JTextField();
    
    // 设置面板背景色
    north.setBackground(new Color(41, 45, 62));// 顶部面板背景色设为深灰色（接近黑色）
    center.setBackground(new Color(240, 240, 240)); // 中部面板背景色设为浅灰色（与窗口背景一致）
    
    // 设置文本框样式
    field.setPreferredSize(new Dimension(700,150));//大小
    field.setEditable(false);//不可编辑
    field.setFont(new Font("SansSerif",Font.BOLD,35));//字体
    field.setBackground(new Color(41, 45, 62));//背景色
    field.setForeground(Color.WHITE);//文字颜色
    field.setHorizontalAlignment(JTextField.RIGHT);//右对齐
    field.setBorder(null);//无边框
    
    // 按钮文本内容
    String[] str=new String[]{
       "7","8","9","=",
       "4","5","6","-",
       "1","2","3","×",
       "0",".","+","÷",
       "C","B","T","S"
    };
    
    // 初始化按钮数组
    btns=new JButton[str.length];
    for(int i=0;i<btns.length;i++){//遍历初始化每个按钮
        btns[i]=new JButton(str[i]);//创建按钮
        btns[i].setFont(new Font("SansSerif",Font.BOLD,25));//设置字体
        btns[i].setFocusPainted(false);// 取消焦点边框
        
        // 按钮颜色设置
        if (str[i].equals("C") || str[i].equals("B")) {// 清空（C）和退格（B）按钮
            btns[i].setBackground(new Color(239, 83, 80));//红色背景（RGB 239,83,80）
            btns[i].setForeground(Color.WHITE);// 白色文字
        } else if (str[i].equals("=")) {
            btns[i].setBackground(new Color(30, 136, 229));//蓝色
            btns[i].setForeground(Color.WHITE);
        } else if (isOperator(str[i].charAt(0))) {// 运算符（+、-、×、÷）按钮（调用isOperator方法判断）
            btns[i].setBackground(new Color(255, 152, 0));// 橙色背景（RGB 255,152,0）
            btns[i].setForeground(Color.WHITE);
        } else if (str[i].equals("T") || str[i].equals("S")) {
            btns[i].setBackground(new Color(123, 31, 162));//紫色背景（RGB 123,31,162）
            btns[i].setForeground(Color.WHITE);
        } else {// 数字按钮（0-9）
            btns[i].setBackground(new Color(66, 66, 66)); // 深灰色背景（RGB 66,66,66）
            btns[i].setForeground(Color.WHITE);
        }
    }
    
    // 布局设置
    north.setLayout(new BorderLayout());
    center.setLayout(new GridLayout(5,4, 10, 10));
    
    // 添加内边距
    north.setBorder(javax.swing.BorderFactory.createEmptyBorder(20, 20, 20, 20));
    center.setBorder(javax.swing.BorderFactory.createEmptyBorder(20, 20, 20, 20));
    
    // 添加组件
    north.add(field,BorderLayout.CENTER);
    for(JButton btn : btns){
        center.add(btn);
    } 
    add(north,BorderLayout.NORTH);
    add(center);
    
    // 绑定按钮事件
    for(JButton btn : btns){
        btn.addActionListener(this);
    }
   }
   
   // 检查是否为操作符
   private boolean isOperator(char c) { // 传入字符，返回是否为运算符
       return c == '+' || c == '-' || c == '×' || c == '÷';// 若字符是+、-、×、÷之一，返回true；否则false
   }
   
   // 播放按键音效
   private void playKeySound() {// 按键被点击时调用，播放音效
       // 使用单独线程播放音效，避免阻塞UI
       new Thread(() -> {// 创建匿名线程，使用Lambda表达式简化 Runnable 接口实现
           try {// 音频操作可能抛出多种异常，需捕获
               // 检查音效文件是否存在
               File soundFile = new File(keySoundPath);// 音效文件
               if (!soundFile.exists()) { // 判断文件是否存在
                   System.out.println("按键音效文件不存在: " + keySoundPath);
                   return;
               }
               
               // 打开并播放音效
               AudioInputStream audioStream = AudioSystem.getAudioInputStream(soundFile); // 获取音频输入流（从文件读取音频数据）
               AudioFormat format = audioStream.getFormat();// 获取音频格式（采样率、位深等信息）
               DataLine.Info info = new DataLine.Info(Clip.class, format); // 创建数据行信息，指定音频格式和Clip类型
                // 尝试获取音频线路（Clip实例），try-with-resources自动关闭资源
               try (Clip soundClip = (Clip) AudioSystem.getLine(info)) {
                   soundClip.open(audioStream);// 打开音频流，加载音频数据
                   soundClip.start();// 开始播放音效
                   
                   // 等待音效播放完成
                   Thread.sleep(soundClip.getMicrosecondLength() / 1000);
               }
           }  // 捕获可能的异常：不支持的音频格式、IO错误、线路不可用、线程中断
           catch (UnsupportedAudioFileException | IOException | LineUnavailableException | InterruptedException e) {
               System.out.println("播放按键音效出错: " + e.getMessage());// 打印错误信息
               e.printStackTrace();// 打印异常堆栈，便于调试
           }
       }).start();// 启动线程（否则线程不会执行）
   }
   
   // 处理按键文本
   private void setBtnText(String text) {
       // 播放按键音效
       playKeySound();
       
       String currentText = field.getText();
       
       switch(text) {
           case "C":// 清除
               field.setText("");
               break;
           case "B":
               if (!currentText.isEmpty()) {
                   field.setText(currentText.substring(0, currentText.length() - 1));
               }
               break;
           case "T":
               SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
               field.setText("当前时间:" + sdf.format(new Date()));
               break;
           case "S":
               playMusic();
               break;
           case "=":
               calculator();
               break;
           case ".":
               handleDecimal(currentText);
               break;
           default:
               handleNumbersAndOperators(currentText, text);
               break;
       }
   }
   
   // 处理小数点逻辑
   private void handleDecimal(String currentText) { // 参数currentText为当前文本框内容
       if (currentText.isEmpty()) {// 若文本框为空（未输入任何内容
           field.setText("0.");// 直接显示"0."（避免单独的"."无法计算）
       } else {
           char lastChar = currentText.charAt(currentText.length() - 1); // 获取最后一个字符
           if (isOperator(lastChar) || lastChar == '(') {// 若最后一个字符是运算符或左括号（如"3+"、"("）
               field.setText(currentText + "0."); // 在后面添加"0."（如"3+"→"3+0."）
           } else if (lastChar != '.') {// 若最后一个字符不是小数点（避免连续输入"."）
            // 按运算符/括号分割文本，取最后一段（当前输入的数字）
               String[] parts = currentText.split("[+\\-×÷()]"); // 用正则表达式分割，分隔符为+、-、×、÷、(、)
               String lastNumber = parts[parts.length - 1];// 最后一段即为当前正在输入的数字（如"3+45"→分割为["3","45"]→lastNumber="45"）
               if (!lastNumber.contains(".")) {// 若当前数字中没有小数点
                   field.setText(currentText + ".");// 可以添加小数点（如"45"→"45."）
               }
               }
           }
       }
   
   
   // 处理数字和操作符
   private void handleNumbersAndOperators(String currentText, String text) {// currentText：当前内容；text：当前点击的按钮文本（数字或运算符）
       if (isOperator(text.charAt(0))) {// 若点击的是运算符（+、-、×、÷）
           if (!currentText.isEmpty()) {// 文本框不为空
               char lastChar = currentText.charAt(currentText.length() - 1);
               if (!isOperator(lastChar) && lastChar != '(') {// 若最后一个字符不是运算符或左括号（如"3"、"5)"）
                   field.setText(currentText + text); // 直接添加运算符（如"3"→"3+"）
               } else if (isOperator(lastChar) && lastChar != '-') {// 若最后一个字符是运算符且不是减号（如"3+"、"5×"）
                   field.setText(currentText.substring(0, currentText.length() - 1) + text);
               }
           } else if (text.equals("-")) {// 文本框为空，且点击的是减号（允许负数开头，如"-3"）
               field.setText(text); // 直接显示"-"
           }
       } else {// 点击的是数字（0-9）
           field.setText(currentText + text);// 直接在当前内容后添加数字（如"3"→"35"）
       }
   }
   
   // 播放音乐方法
   private void playMusic() { // 点击"S"按钮时调用
       try {// 音频操作可能抛出异常
           if (clip == null) { // 若clip未初始化（首次播放音乐）
               File audioFile = new File(musicFilePath);// 根据路径创建音乐文件对象
               if (!audioFile.exists()) {// 文件不存在
                   field.setText("音乐文件不存在"); // 文本框显示提示
                   return;
               }
               // 获取音频输入流
               AudioInputStream audioStream = AudioSystem.getAudioInputStream(audioFile);
               AudioFormat format = audioStream.getFormat(); // 获取音频格式
               DataLine.Info info = new DataLine.Info(Clip.class, format);// 创建数据行信息
               
               clip = (Clip) AudioSystem.getLine(info);// 初始化clip对象
               clip.open(audioStream);// 打开音频流
               clip.start();// 开始播放
               field.setText("音乐播放中...");
           } else if (clip.isRunning()) { // 若clip已初始化且正在播放（isRunning()返回true）
               clip.stop();
               field.setText("音乐已暂停");
           } else { // 若clip已初始化但未播放（处于暂停状态）
               clip.start();
               field.setText("音乐播放中...");
           }
       } catch (Exception e) {// 捕获所有可能的异常（简化代码，实际可细分）
           field.setText("播放音乐出错");
           e.printStackTrace();// 打印异常信息
       }
   }
   
   // 计算逻辑
  private void calculator() {
       // 用户输入1+1 我们执行计算操作 然后将结果展示到文本框
       // 如果表达式里有× ÷ 要替换成* /
       String expression = field.getText();
       expression = expression.replace("×", "*").replace("÷", "/");
       
       // 确保表达式以数字结尾，而不是操作符
       if (!expression.isEmpty() && isOperator(expression.charAt(expression.length() - 1))) {
           expression = expression.substring(0, expression.length() - 1);
       }
       
       // 我们执行操作 使用js引擎
       ScriptEngineManager manager = new ScriptEngineManager();
       // 初始化计算引擎 使用javascript解析数学表达式
       ScriptEngine engine = manager.getEngineByName("javascript");
       try {
           // 开始计算 计算过程可能出现异常 需要处理异常
           Object result = engine.eval(expression);
           // 然后将结果展示到文本框
           field.setText(result.toString());
       } catch (ScriptException e) {
           // catch中放的是异常处理方式
           field.setText("运算错误");
           e.printStackTrace();
       }
   }

   @Override
   public void actionPerformed(ActionEvent a) {
       Object source = a.getSource();
       if (source instanceof JButton) {
           JButton btn = (JButton) source;
           setBtnText(btn.getText());
       }
   }
   
   @Override// 重写JFrame的dispose方法（窗口关闭时调用）
   public void dispose() {
       if (clip != null && clip.isOpen()) {// 若音频剪辑已打开
           clip.close(); // 关闭音频剪辑，释放资源（避免内存泄漏）
       }
       super.dispose();// 调用父类的dispose方法，释放窗口资源
   }
   
    // 程序入口，JVM首先执行
   public static void main(String[] args) {
    // 在Swing事件调度线程中创建计算器实例
       SwingUtilities.invokeLater(Calculator::new);
   }
}